/*
	Работа с Ethernet
*/

#include "defines.h"

//Наши сокеты
U8 tcpServer;
U8 udpServer;

U8 udpActive;
//Таймеры
U64 tcpTime=0;

//Наши буферы
U8 tcpServer_recvbuffer[2*1024];
U16 tcpServer_recvbufcount=0;

U8 udpServer_recvbuffer[2*1024];
U16 udpServer_recvbufcount=0;

//А кто к нам обратился по UDP
U8 udpClient_ip[4];
U16 udpClient_port;


//Это все сетевые параметры, которые надо задать до инициализации
extern U8 own_hw_adr[];
extern U8 lhost_name[];
extern LOCALM localm[];

#define TCP_SERVER_PORT 10000
#define UDP_SERVER_PORT	10001

NetParamsT defaultNetParams = {{192,168,0,10},{192,168,0,1},{255,255,255,0}};
NetParamsT *netParams = &defaultNetParams;
U8 * mac  = (U8 *)MAC_ADDRESS;

U8 buf[FILE_BLOCK_SIZE];

/*
	Настройка Ethernet
*/
void ethernet_init()
{
	//тут надо настроить сетевые параметры
	memcpy(own_hw_adr,mac,6);
	memcpy(&localm[NETIF_ETH],netParams,sizeof(NetParamsT));
	
	//Настройка Ethernet
	init_TcpNet();
	//Инициализация необходимых сокетов
	sockets_init();
}

/*
	Создаем Tcp сокет для подключения к сокету
*/
void sockets_init(void)
{
	//TCP server
	tcpServer = tcp_get_socket (TCP_TYPE_SERVER|TCP_TYPE_KEEP_ALIVE, 0, 30, tcpServer_callback);
  tcp_listen(tcpServer,TCP_SERVER_PORT);
	
	//UDP Server
	udpServer = udp_get_socket(0,UDP_OPT_SEND_CS | UDP_OPT_CHK_CS, udpServer_callback);
	udp_open(udpServer,UDP_SERVER_PORT);
	
	
}	
/*
	TCP/IP цикл
*/
void tcpip_prc(void)
{
	
	//Тикаем каждые 100мс
	if (SystemTick-tcpTime>=100)
	{
		tcpTime = SystemTick;
		timer_tick();
	}		
	//Тут обрабатываем tcp\ip пакеты
	main_TcpNet();
	
	//Приняли данные по TCP?
	if (tcpServer_recvbufcount)
	{
		udpActive=0;
		//Парсим данные
		ethernet_parse_packet(tcpServer_recvbuffer,tcpServer_recvbufcount,tcpServer);
		//Обнуляем счетчики
		tcpServer_recvbufcount=0;
		
	}
	//А может по UDP есть?
	if(udpServer_recvbufcount)
	{
		udpActive=1;
		ethernet_parse_packet(udpServer_recvbuffer,udpServer_recvbufcount,udpServer);
		udpServer_recvbufcount=0;
	}
	
}


/*
	tcpServer Call Back 
*/
U16 tcpServer_callback (U8 soc, U8 event, U8 *ptr, U16 par) 
{
  
	if (soc != tcpServer)
    return (0);
  

	/* This function is called on TCP event */
    switch (event) {
    case TCP_EVT_CONREQ:
      /* Remote host is trying to connect to our TCP socket. */
      /* 'ptr' points to Remote IP, 'par' holds the remote port. */
       
      /* Return 1 to accept connection, or 0 to reject connection */
      return (1);
    case TCP_EVT_ABORT:
      /* Connection was aborted */
			
	    break;
    case TCP_EVT_CONNECT:
			return 1; 
      break;
    case TCP_EVT_CLOSE:
      /* Connection has been closed */
       
      break;
    case TCP_EVT_ACK:
      /* Our sent data has been acknowledged by remote peer */
       return 1;
      break;
    case TCP_EVT_DATA:
      /* TCP data frame has been received, 'ptr' points to data */
      /* Data length is 'par' bytes */
			memcpy(tcpServer_recvbuffer,ptr,par);
			tcpServer_recvbufcount=par;
				
			return 1;
      break;
  }
  return (0);
}

/*
	Парсим данные
*/

void ethernet_parse_packet(U8 * packet, U16 packet_size, U8 socket)
{
	//Вычисляем значение поля length
	U16 length = *((U16 *)&packet[LINK_LENGTH]);

	//Пакет должен как минимимум содержать заголовок		
	if ((length>=LINK_HEADER_SIZE)&&(packet_size>=length))
	{
		
		//Смотрим коды команд
		switch(packet[LINK_TYPE])
		{
			//case CMD_CHECK_LINK: 					cmd_check_link(packet, length,socket); 			break;
			case CMD_GET_INFO:						cmd_get_info(packet,length,socket);			break;
			
			
			case CMD_WRITE_FILE_BLOCK:		cmd_write_file_block(packet,length,socket);		break;
			case CMD_WRITE_FILE_INFO:			cmd_write_file_info(packet,length,socket);				break;
			case CMD_JUMP_TO_APPLICATION:	cmd_jumpto_application(packet,length,socket);		break;
			case CMD_JUMP_TO_BOOTLOADER:	ans_ok(packet[LINK_TYPE],socket);		break;	
						
			default:		ans_error(packet[LINK_TYPE],ERROR_NOT_SUPPORTED,socket);		break;
			
		}
	}
	//Посылаем ошибку
	else
		ans_error(packet[LINK_TYPE],ERROR_LENGTH_FIELD,socket);
}
/*
	Выдадим информацию об устройстве
*/
void cmd_get_info(U8 * packet, U16 length, U8 socket)
{
	SER_PutStr("get info\r\n");
	if (length==LINK_HEADER_SIZE)
	{
		char str[100];
		char * p = FW_VER;
		memset(str,0x00,sizeof(str));
		sprintf(str,"FW=%s\r\nSN=%02X%02X%02X%02X%02X%02X\r\n",p,own_hw_adr[0],own_hw_adr[1],own_hw_adr[2],own_hw_adr[3],own_hw_adr[4],own_hw_adr[5]);
		//Отправлеяем ответ
		link_send_packet(ANS_INFO,str,strlen(str),socket);
	}
	else
		ans_error(packet[LINK_TYPE],ERROR_LENGTH_FIELD,socket);
}
void cmd_write_file_block(U8 * packet, U16 length, U8 socket)
{
		SER_PutStr("write file block\r\n");
		if (length==LINK_HEADER_SIZE+FILE_BLOCK_SIZE+4)
		{
			//Куда писать?	
			U32 address=0;
			U8 num=0;
			U32 temp=0;
			temp = packet[LINK_DATA];
			address+=temp;
			
			temp = packet[LINK_DATA+1];
			address+=temp<<8;
			temp = packet[LINK_DATA+2];
			address+=temp<<16;
			temp = packet[LINK_DATA+3];
			address+=temp<<24;
						
			num = 	USER_SECTOR_START+address/SECTOR_SIZE;
			//Надо стирать сектор
			if (address%SECTOR_SIZE==0)
			{
				prepare_sector(num,num);
				erase_sector(num,num);
			}
			//А теперь пишем данные
			prepare_sector(num,num);
			
			memcpy(buf,(U8 *)&packet[LINK_DATA+4],FILE_BLOCK_SIZE);
			//write_data(USER_FLASH_START + address,(U32 *)((U8 *)&packet[LINK_DATA+4]),FILE_BLOCK_SIZE);
			write_data(USER_FLASH_START + address,(U32 *)buf,FILE_BLOCK_SIZE);
			
			//Ответ
			ans_ok(packet[LINK_TYPE],socket);
			
		}	
		else
			ans_error(packet[LINK_TYPE],ERROR_LENGTH_FIELD,socket);
}

/*
	Записываем информацию о приложении - размер и контрольную сумму
*/
void cmd_write_file_info(U8 * packet, U16 length, U8 socket)
{
	SER_PutStr("write file info\r\n");
	if (length==LINK_HEADER_SIZE+sizeof(AppInfoT))
	{
		//Сначала стираем сектор
		prepare_sector(APP_INFO_SECTOR,APP_INFO_SECTOR);
		erase_sector(APP_INFO_SECTOR,APP_INFO_SECTOR);
		
		//А теперь пишем
		memset(buf,0x00,FILE_BLOCK_SIZE);
		memcpy((U8 *)&buf,(U8 *)&packet[LINK_DATA],sizeof(AppInfoT));
		prepare_sector(APP_INFO_SECTOR,APP_INFO_SECTOR);
		write_data(APP_INFO_ADDRESS,(U32 *)buf,FILE_BLOCK_SIZE);
		
		//Ответ
		ans_ok(packet[LINK_TYPE],socket);
	}
	else
		ans_error(packet[LINK_TYPE],ERROR_LENGTH_FIELD,socket);
}

/*
	Прыгаем на приложение
*/
void cmd_jumpto_application(U8 * packet, U16 length, U8 socket)
{
	SER_PutStr("jump to application\r\n");
	if (length==LINK_HEADER_SIZE)
	{
			//останавливаем таймер
			system_timer_stop();
			//А теперь прыгаем
			execute_user_code();
		
			//Ответа нет...
	}	
	else
		ans_error(packet[LINK_TYPE],ERROR_LENGTH_FIELD,socket);	
	
}

/*
	Отправляем ОК
*/
void ans_ok(U8 type,U8 sock)
{
	link_send_packet(ANS_OK,(U8*)&type,sizeof(type),sock);
}

/*
	Отправляем ошибку
*/
void ans_error(U8 type, U8 error,U8 sock)
{
	U8 err[2];
	err[0] = type;
	err[1] = error;
	link_send_packet(ANS_ERROR,(U8*)&err,sizeof(err),sock);
}



void link_send_packet(uint8_t type, uint8_t * data, uint16_t data_length,U8 sock)
{
	//Выделяем память под новый пакет = данные + заголовок
	uint8_t packet[260];
	
	//Формируем поле LENGTH
	uint16_t length= data_length+LINK_HEADER_SIZE;
	memcpy(packet, (uint8_t *)&(length),sizeof(length));
	//Поле type
	packet[LINK_TYPE] = type;

	//Копируем данные 
	memcpy(packet+LINK_DATA,data,data_length);

	//Отправляем пакет
	if (!udpActive)
		tcp_send_data(packet,data_length+LINK_HEADER_SIZE,sock);
	else
		udp_send_data(packet,data_length+LINK_HEADER_SIZE,sock);
}

U8 tcp_send_data(U8 * data, U16 size, U8 sock)
{
	U8 *sendbuf;
  U16 maxlen;	
	
	if (tcp_check_send (sock))
	{
		//The socket is ready to send the data. 
		maxlen = tcp_max_dsize (sock);
		sendbuf = tcp_get_buf (maxlen);
		memcpy(sendbuf,data,size);
		tcp_send (sock, sendbuf, size);
		return 1;
	}
	else 
		return 0;
	
}

U16 udpServer_callback (U8 socket, U8 *remip, U16 port, U8 *buf, U16 len)  
{
  /* This function is called when UDP data has been received. */
	memcpy(udpClient_ip,remip,sizeof(udpClient_ip));
	udpClient_port = port;
	
	memcpy(udpServer_recvbuffer,buf,len);
	udpServer_recvbufcount = len;
  
  return (0);
}

U8 udp_send_data(U8 * data, U16 size, U8 sock)
{
	U8 * send_buf = udp_get_buf (size);
	memcpy(send_buf,data,size);
	udp_send(sock,udpClient_ip,udpClient_port,send_buf,size);
}